import cgi
import os
import re
import time
import datetime
import wsgiref.handlers

from google.appengine.api import mail
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from diff import textDiff
from settings import email
from settings import domain
from settings import appname
from settings import analytics

# Handles the index page display
class IndexPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		recent_posts = db.Query (Post).filter ('status =', 'live').order ('-date').fetch (limit=10)
		tag_list = db.Query (Tag).order ('-frequency').fetch (limit=50)

		template_values = {
			'recent_posts': recent_posts,
			'tags': tag_list,
			'user': user,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'index.html')
		self.response.out.write (template.render (path, template_values))

# Creates a new post.  If the user is not logged in, set it to pending, then authenticate and approve.
class PostPage (webapp.RequestHandler):
	def post (self):
		user = users.get_current_user ()
		if user:
			# save as normal and forward to /post
			rid = re.compile ('[^a-z0-9-]+')
			pid = self.request.get ('title').lower ()
			pid = rid.sub ('-', pid)

			post = Post (
				id=pid,
				title=self.request.get ('title'),
				body=self.request.get ('body'),
				status='live',
				author=user,
				tags=re.split ('[\W]+', self.request.get ('tags'))
			)
			post.put ()

			history = History (
				id=pid,
				title=self.request.get ('title'),
				body=self.request.get ('body'),
				author=user
			)
			history.put ()

			for name in post.tags:
				tag_update_freq (name)

			if self.request.get ('email') != "":
				mail.send_mail (
					email (),
					self.request.get ('email'),
					'[' + pid + '] Post from ' + user.nickname (),
					"""
%s posted the following comment or question for you:

#
%s
#

To reply, please visit:

http://%s/post?pid=%s

- %s
""" % (user.nickname (), self.request.get ('body'), domain (), pid, appname ())
				)

			self.redirect ('/post?pid=' + pid)
		else:
			# save with pending status and login with redirect to /post
			rid = re.compile ('[^a-z0-9-]+')
			pid = self.request.get ('title').lower ()
			pid = rid.sub ('-', pid)

			post = Post (
				id=pid,
				title=self.request.get ('title'),
				body=self.request.get ('body'),
				status='pending',
				tags=re.split ('[\W]+', self.request.get ('tags'))
			)
			post.put ()

			self.redirect (users.create_login_url ('/post?newpid=' + pid + '&email=' + self.request.get ('email')))

	def get (self):
		user = users.get_current_user ()
		if user:
			# if there's a pid, update and add history then forward to /post?pid=...
			pid = self.request.get ('newpid')
			send_to = self.request.get ('email')
			
			if pid:
				q = db.Query (Post)
				q.filter ('id =', pid)
				post = q.get ()
				post.author = user
				post.status = 'live'
				post.put ()
	
				history = History (
					id=pid,
					title=post.title,
					body=post.body,
					author=user
				)
				history.put ()

				for name in post.tags:
					tag_update_freq (name)

				if send_to != "":
					mail.send_mail (
						email (),
						send_to,
						'[' + pid + '] Post from ' + user.nickname (),
						"""
%s posted the following comment or question for you:

#
%s
#

To reply, please visit:

http://%s/post?pid=%s

- %s
	""" % (user.nickname (), post.body, domain (), pid, appname ())
					)

				# send to standard page
				self.redirect ('/post?pid=' + pid)
				return

		# show the standard page
		pid = self.request.get ('pid')

		if not pid:
			self.redirect ('/')

		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		if post == None:
			self.redirect ('/')

		template_values = {
			'post_id': post.id,
			'post_title': post.title,
			'post_body': body_filter (post.body),
			'post_tags': post.tags,
			'post_date': post.date,
			'user': user,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'post.html')
		self.response.out.write (template.render (path, template_values))

# This appends a quick update to a post and emails the previous editors of the post
class UpdatePage (webapp.RequestHandler):
	def post (self):
		user = users.get_current_user ()

		if user:
			pid = self.request.get ('pid')
	
			q = db.Query (Post)
			q.filter ('id =', pid)
			post = q.get ()
	
			if post == None:
				self.redirect ('/')
	
			post.body = post.body + "\n---\n" + self.request.get ('body')
			post.put ()
	
			history = History (
				id=pid,
				title=post.title,
				body=post.body,
				author=user
			)
			history.put ()

			# mail users from this page's history that there's been an update
			q = History.all ()
			q.filter ('id =', pid)

			sent = [user]

			subject_line = '[' + pid + '] Post Updated'
			message_body = """
Just letting you know that an update has been made to a post that you made or edited.  To view the updated post, you can use the following link:

http://%s/post?pid=%s

- %s
""" % (domain (), pid, appname ())

			for entry in q:
				if entry.author not in sent:
					#logging.debug ('mailing ' + entry.author.email ())
					mail.send_mail (
						email (),
						entry.author.email (),
						subject_line,
						message_body
					)
					sent += [entry.author]

			self.redirect ('/post?pid=' + pid)
			return

		else:
			self.redirect ('/')

# This shows and handles the full post edit form and emails the previous editors of the post
class UpdateFullPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		if not user:
			self.redirect ('/')

		pid = self.request.get ('pid')

		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		if post == None:
			self.redirect ('/')

		template_values = {
			'post_id': post.id,
			'post_title': post.title,
			'post_body': post.body,
			'user': user,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'update-full.html')
		self.response.out.write (template.render (path, template_values))

	def post (self):
		user = users.get_current_user ()

		if not user:
			self.redirect ('/')

		pid = self.request.get ('pid')

		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		if post == None:
			self.redirect ('/')

		post.body = self.request.get ('body')
		post.put ()

		# update history
		history = History (
			id=pid,
			title=post.title,
			body=post.body,
			author=user
		)
		history.put ()

		# mail users from this page's history that there's been an update
		q = History.all ()
		q.filter ('id =', pid)

		sent = [user]

		subject_line = '[' + pid + '] Post Updated'
		message_body = """
Just letting you know that an update has been made to a post that you made or edited.  To view the updated post, you can use the following link:

http://%s/post?pid=%s

- %s
""" % (domain (), pid, appname ())

		for entry in q:
			if entry.author not in sent:
				#logging.debug ('mailing ' + entry.author.email ())
				mail.send_mail (
					email (),
					entry.author.email (),
					subject_line,
					message_body
				)
				sent += [entry.author]

		self.redirect ('/post?pid=' + pid)

# List all pages tagged with the specified tag, sorted by date or alphabetically
class TagPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		sort = self.request.get ('sort')
		if sort == 'alpha':
			posts = db.Query (Post).filter ('tags =', self.request.get ('tag')).order ('title').fetch (limit=50)
		else:
			posts = db.Query (Post).filter ('tags =', self.request.get ('tag')).order ('-date').fetch (limit=50)

		recent_posts = db.Query (Post).filter ('status =', 'live').order ('-date').fetch (limit=10)
		tag_list = db.Query (Tag).order ('-frequency').fetch (limit=50)

		template_values = {
			'posts': posts,
			'user': user,
			'tag': self.request.get ('tag'),
			'tags': tag_list,
			'recent_posts': recent_posts,
			'sort': sort,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'tag.html')
		self.response.out.write (template.render (path, template_values))

# Add a tag to the specified post, could use AJAX in the future...
class TagAddPage (webapp.RequestHandler):
	def post (self):
		pid = self.request.get ('pid')

		if not pid:
			self.redirect ('/')

		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		tags=re.split ('[\W]+', self.request.get ('tags'))
		for tag in tags:
			if tag not in post.tags:
				post.tags.append (tag)

				tag_update_freq (tag)

		post.put ()

		self.redirect ('/post?pid=' + pid)

# Show the history of changes to the specified post
class HistoryPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		pid = self.request.get ('pid')

		if not pid:
			self.redirect ('/')

		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		if post == None:
			self.redirect ('/')

		history = db.Query (History).filter ('id =', pid).order ('-date').fetch (limit=50)

		template_values = {
			'post_id': post.id,
			'post_title': post.title,
			'post_body': body_filter (post.body),
			'post_tags': post.tags,
			'post_date': post.date,
			'user': user,
			'history': history,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'history.html')
		self.response.out.write (template.render (path, template_values))

# Compare a previous version to the current version of the specified post
class HistoryComparePage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		pid = self.request.get ('pid')

		if not pid:
			self.redirect ('/')

		key = self.request.get ('version')

		if not key:
			self.redirect ('/')

		# 1. get current
		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		# 2. get history
		version = History.get (key)

		# 3. compare
		body = textDiff (body_filter (version.body), body_filter (post.body))

		# 4. output
		template_values = {
			'post_id': post.id,
			'post_title': post.title,
			'post_body': body,
			'post_tags': post.tags,
			'post_date': version.date,
			'version': key,
			'user': user,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'compare.html')
		self.response.out.write (template.render (path, template_values))

# Restore a previous version of the specified post
class HistoryRestorePage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		if not user:
			self.redirect ('/')

		pid = self.request.get ('pid')

		if not pid:
			self.redirect ('/')

		key = self.request.get ('version')

		if not key:
			self.redirect ('/')

		# 1. get history
		version = History.get (key)

		# 2. get current
		q = db.Query (Post)
		q.filter ('id =', pid)
		post = q.get ()

		# 3. update current
		post.body = version.body
		post.author = user
		post.put ()

		# 4. add to history
		history = History (
			id=pid,
			title=post.title,
			body=post.body,
			author=user
		)
		history.put ()

		# 5. send to /post
		self.redirect ('/post?pid=' + pid)

# Send the user to the login page, but set the redirect correctly if they're on
# a post
class LoginPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()
		if user:
			self.redirect ('/')
		else:
			pid = self.request.get ('pid')

			if pid:
				self.redirect (users.create_login_url ('/post?pid=' + pid))
			else:
				self.redirect (users.create_login_url ('/'))

# Do a logout if the user is logged in
class LogoutPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()
		if not user:
			self.redirect ('/')
		else:
			self.redirect (users.create_logout_url ('/'))

# Display the tooltip page
class TooltipPage (webapp.RequestHandler):
	def get (self):
		user = users.get_current_user ()

		recent_posts = db.Query (Post).filter ('status =', 'live').order ('-date').fetch (limit=10)
		tag_list = db.Query (Tag).order ('-frequency').fetch (limit=50)

		template_values = {
			'recent_posts': recent_posts,
			'tags': tag_list,
			'user': user,
			'appname': appname (),
			'analytics': analytics (),
		}

		path = os.path.join (os.path.dirname (__file__), 'tooltip.html')
		self.response.out.write (template.render (path, template_values))

# This is the post object type in the datastore
class Post (db.Model):
	id = db.StringProperty (required=True)
	title = db.StringProperty (required=True)
	body = db.TextProperty (required=True)
	status = db.StringProperty (required=True)
	author = db.UserProperty ()
	date = db.DateTimeProperty (auto_now=True)
	tags = db.StringListProperty ()

# This holds the history of changes for posts in the datastore
class History (db.Model):
	id = db.StringProperty (required=True)
	title = db.StringProperty (required=True)
	body = db.TextProperty (required=True)
	author = db.UserProperty (required=True)
	date = db.DateTimeProperty (auto_now_add=True)

# These are the tags in the datastore
class Tag (db.Model):
	name = db.StringProperty (required=True)
	frequency = db.IntegerProperty (required=True)

# A quick and dirty wiki-like syntax parser.  Should have used a 3rd-party lib
# but I didn't want to spend too much time searching for one, since this was a
# learning project for the App Engine framework.
def body_filter (value):
	value = cgi.escape (value)

	rid = re.compile ('(\r\n|\n\r|\r|\n)+---*(\r\n|\n\r|\r|\n)+')
	value = rid.sub ('<hr />\n', value)

	rid = re.compile ('&lt;(/?)(embed|object|param)(.*?)&gt;')
	value = rid.sub ('<\\1\\2\\3>', value)

	rid = re.compile ('\[([^ ]+) ([^\]]+)\]')
	value = rid.sub ('<a href="\\1">\\2</a>', value)

	rid = re.compile ('(\r\n|\n\r|\r|\n)')
	value = rid.sub ("<br />\n", value)

	rid = re.compile ('<hr /><br />')
	value = rid.sub ('<hr />', value)

	rid = re.compile ('([^"])(ftp|http|https):\/\/([^ \t\r\n<]+)')
	value = rid.sub ('\\1<a href="\\2://\\3">\\2://\\3</a>', value)

	rid = re.compile ('\n\* ([^\n]+)')
	value = rid.sub ('\n<li>\\1</li>', value)

	rid = re.compile ('\n# ([^\n]+)')
	value = rid.sub ('\n<h3>\\1</h3>', value)

	rid = re.compile ('<br \/><\/h3>')
	value = rid.sub ('</h3>', value)

	rid = re.compile ('\*(.+?)\*')
	value = rid.sub ('<strong>\\1</strong>', value)

	rid = re.compile ('\|(.+?)\|')
	value = rid.sub ('<tt>\\1</tt>', value)

	rid = re.compile ('\/\/([a-zA-Z0-9 \'"\?\._-]+)\/\/')
	value = rid.sub ('<em>\\1</em>', value)

	return value;

# Increases a tag's frequency by one each time it's added to a page
def tag_update_freq (name):
	q = db.Query (Tag)
	q.filter ('name =', name)
	tag = q.get ()

	if tag == None:
		tag = Tag (
			name=name,
			frequency=1
		)
		tag.put ()

	else:
		tag.frequency += 1
		tag.put ()

# Main maps requests to the handlers above
def main ():
	application = webapp.WSGIApplication (
		[
			('/', IndexPage),
			('/post', PostPage),
			('/update', UpdatePage),
			('/update-full', UpdateFullPage),
			('/history', HistoryPage),
			('/history-compare', HistoryComparePage),
			('/history-restore', HistoryRestorePage),
			('/tag', TagPage),
			('/tag-add', TagAddPage),
			('/login', LoginPage),
			('/logout', LogoutPage),
			('/tooltip', TooltipPage)
		],
		debug=True
	)
	wsgiref.handlers.CGIHandler ().run (application)


if __name__ == '__main__':
	main ()
